Falle beim Left Outer Join
Häufig sind es die Kleinigkeiten, die einem das Leben schwer machen. Die berühmten letzten 20%. Häufig bemerkt man jedoch gar nicht, dass man überhaupt ein Problem hat, weil alles scheinbar so funktioniert, wie man es sich vorstellt.
Aufgabe: SELECT
Die Aufgabe sollte sein: Selektiere aus Tabelle ZZT1 alle Einträge anhand der gegebenen Selektionskriterien (AREA). Lies zusätzlich mit einem LEFT OUTER JOIN alle Einträge aus Tabelle ZZT2, die über das Feld LINK verknüpft sind hinzu. Einträge aus Tabelle ZZT2 mit gesetztem Löschkennzeichen dürfen nicht berücksichtigt werden.
Demodaten
Die folgenden Demodaten stehen zur Verfügung:
- Tabelle ZZT1
- Tabelle ZZT2
Selektionskriterium soll sein: AREA = TEST
Tabelle ZZT1 (links)
KEY1 | TEXT | LINK | AREA |
1 | Eins | A | TEST |
2 | Zwei | B | TEST |
3 | Drei | C | TEST |
4 | Vier | TEST | |
5 | Fünf | A | NEU |
Tabelle ZTT2 (rechts)
LINK | TEXT | LOEVM |
A | Info A | |
B | Info B | X |
C | Info C |
Überlegungen
Wenn aus Tabelle ZZT1 alle Einträge selektiert werden, bei denen das Feld AREA mit “TEST” gefüllt ist, dann erwarte ich, dass die folgenden Einträge selektiert werden: 1, 2, 3 und 4.
Zusätzlich muss bei den Einträgen 1 und 3 der LINK_TEXT gefüllt sein, denn diese Einträge verweisen auf Einträge in Tabelle ZZT2, die keine Löschvormerkung haben.
Erster Versuch
Der erste Wurf sieht folgendermaßen aus:
SELECT z1~key1, z1~text, z1~link, z1~area, z2~text AS link_text FROM zzt1 AS z1 LEFT OUTER JOIN zzt2 AS z2 ON z1~link = z2~link INTO TABLE @DATA(t_data) WHERE z1~area = 'TEST' AND z2~loevm = @space.
Der SELECT ist syntaktisch (!) fehlerfrei, liefert jedoch leider ein falsches Ergebnis:
KEY1 | TEXT | LINK | AREA | LINK_TEXT |
1 | Eins | A | TEST | Info A |
3 | Drei | C | TEST | Info C |
Zweiter Versuch
Ohne die Einschränkung über die WHERE-Bedingung auf der rechten Seite (AND z2~loevm = @space) funktioniert die Selektion wie erwartet.
Das ist nicht ganz das, was ich erwartet habe… Ich habe mich dann damit beschäftigt, warum das Ergebnis so aussieht und warum Ergebniszeilen fehlen. Beziehungsweise habe ich zuerst versucht, das richtige Ergebnis zu bekommen. Der richtige Select lautet so:
SELECT z1~key1, z1~text, z1~link, z1~area, z2~text AS link_text FROM zzt1 AS z1 LEFT OUTER JOIN zzt2 AS z2 ON z1~link = z2~link AND z2~loevm = @space INTO TABLE @DATA(t_data) WHERE z1~area = 'TEST'.
Ergebnis:
KEY1 | TEXT | LINK | AREA | LINK_TEXT |
1 | Eins | A | TEST | Info A |
2 | Zwei | B | TEST | |
3 | Drei | C | TEST | Info C |
4 | Vier | TEST |
Lessons learned
Bei einem LEFT OUTER JOIN darf die WHERE-Bedingung keine Einschränkung auf die rechte Tabelle haben. In der Hilfe zum JOIN steht:
Eine WHERE-Bedingung für eine SELECT-Anweisung mit Joins wirkt auf die durch die Joins gebildete Ergebnismenge.
Ich deute das so, dass die WHERE-Bedingung sozusagen erst nachträglich angewendet wird. Wobei das für mich ein Zirkelschluss ist, den ich nicht verstehe. Zudem enthält die Ergebnismenge ja gar kein Feld “LOEVM”.
Auf jeden Fall sollte man die Verwendung beziehungsweise die gesammelten Daten eines LEFT OUTER JOINS sehr genau prüfen. Schnell schleicht sich hier ein Fehler ein, mit dem man nicht gerechnet hat.
- Interview mit Björn Schulz (Software-Heroes.com) - 3. September 2024
- Daten aus ALV ermitteln - 3. September 2024
- So lange es den SAPGUI noch gibt… - 27. Juni 2024
Ich hätte das erste Ergebnis auch so erwartet, aber es ist auch eine Sache der Sichtweise. Für mich bedeutet die WHERE-Bedingung immer die Entscheidung, ob ein kompletter Datensatz angezeigt wird oder nicht. Es gibt keine WHERE-Bedingung zur Ermittlung eines “halben” Datensatzes. Und das war – soweit ich es verstanden habe – eigentlich deine Anforderung: Anhand einer Bedingung sollte entschieden werden, ob noch über einen JOIN weitere Daten dazu gelesen werden oder nicht, unabhängig davon, ob grundsätzlich der betreffende Datensatz angezeigt wird. Mit dieser Sichtweise macht es auch Sinn die Bedingung im JOIN-Statement zu platzieren.